/**
 * Code Execution Tools
 * 
 * Tools for executing code in various languages
 * Uses Bun's native capabilities for JavaScript/TypeScript execution
 */

import { z } from 'zod';
import { spawn } from 'child_process';
import { writeFile, unlink } from 'fs/promises';
import { join } from 'path';
import { tmpdir } from 'os';
import type { ToolDefinition } from '@/types';
import { createTextResult, createErrorResult } from '../tool-factory';
import { logger } from '@/utils/logger';

/**
 * Execute JavaScript/TypeScript code using Bun
 */
export const executeJavaScriptTool: ToolDefinition = {
  name: 'execute_javascript',
  description: 'Execute JavaScript or TypeScript code using Bun runtime. Returns the output of the code.',
  inputSchema: z.object({
    code: z.string().describe('JavaScript or TypeScript code to execute'),
    timeout: z.number().optional().default(5000).describe('Execution timeout in milliseconds'),
  }),
  handler: async (args) => {
    try {
      const { code, timeout } = args;
      
      logger.info('Executing JavaScript code');
      
      // Create a temporary file
      const tempFile = join(tmpdir(), `cagent-${Date.now()}.ts`);
      await writeFile(tempFile, code);
      
      try {
        const output = await executeCommand('bun', ['run', tempFile], timeout);
        return createTextResult(`Output:\n\n${output}`);
      } finally {
        // Clean up temp file
        await unlink(tempFile).catch(() => {});
      }
    } catch (error) {
      logger.error('Failed to execute JavaScript code', error);
      return createErrorResult(error as Error);
    }
  },
};

/**
 * Execute Python code
 */
export const executePythonTool: ToolDefinition = {
  name: 'execute_python',
  description: 'Execute Python code. Returns the output of the code.',
  inputSchema: z.object({
    code: z.string().describe('Python code to execute'),
    timeout: z.number().optional().default(5000).describe('Execution timeout in milliseconds'),
  }),
  handler: async (args) => {
    try {
      const { code, timeout } = args;
      
      logger.info('Executing Python code');
      
      // Create a temporary file
      const tempFile = join(tmpdir(), `cagent-${Date.now()}.py`);
      await writeFile(tempFile, code);
      
      try {
        const output = await executeCommand('python3', [tempFile], timeout);
        return createTextResult(`Output:\n\n${output}`);
      } finally {
        // Clean up temp file
        await unlink(tempFile).catch(() => {});
      }
    } catch (error) {
      logger.error('Failed to execute Python code', error);
      return createErrorResult(error as Error);
    }
  },
};

/**
 * Execute shell command
 */
export const executeShellTool: ToolDefinition = {
  name: 'execute_shell',
  description: 'Execute a shell command. Use with caution as this can execute any system command.',
  inputSchema: z.object({
    command: z.string().describe('Shell command to execute'),
    args: z.array(z.string()).optional().default([]).describe('Command arguments'),
    timeout: z.number().optional().default(5000).describe('Execution timeout in milliseconds'),
  }),
  handler: async (args) => {
    try {
      const { command, args: cmdArgs, timeout } = args;
      
      logger.info(`Executing shell command: ${command} ${cmdArgs.join(' ')}`);
      
      const output = await executeCommand(command, cmdArgs, timeout);
      return createTextResult(`Output:\n\n${output}`);
    } catch (error) {
      logger.error('Failed to execute shell command', error);
      return createErrorResult(error as Error);
    }
  },
};

/**
 * Helper function to execute a command
 */
function executeCommand(
  command: string,
  args: string[],
  timeout: number
): Promise<string> {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args);
    
    let stdout = '';
    let stderr = '';
    
    child.stdout.on('data', (data) => {
      stdout += data.toString();
    });
    
    child.stderr.on('data', (data) => {
      stderr += data.toString();
    });
    
    const timeoutId = setTimeout(() => {
      child.kill();
      reject(new Error(`Command timed out after ${timeout}ms`));
    }, timeout);
    
    child.on('close', (code) => {
      clearTimeout(timeoutId);
      
      if (code === 0) {
        resolve(stdout || stderr);
      } else {
        reject(new Error(`Command failed with code ${code}:\n${stderr}`));
      }
    });
    
    child.on('error', (error) => {
      clearTimeout(timeoutId);
      reject(error);
    });
  });
}

/**
 * Evaluate mathematical expression
 */
export const evaluateExpressionTool: ToolDefinition = {
  name: 'evaluate_expression',
  description: 'Safely evaluate a mathematical or logical expression using JavaScript.',
  inputSchema: z.object({
    expression: z.string().describe('Expression to evaluate'),
  }),
  handler: async (args) => {
    try {
      const { expression } = args;
      
      logger.info(`Evaluating expression: ${expression}`);
      
      // Create a safe evaluation context
      const code = `
        const result = ${expression};
        console.log(JSON.stringify(result));
      `;
      
      const tempFile = join(tmpdir(), `cagent-eval-${Date.now()}.js`);
      await writeFile(tempFile, code);
      
      try {
        const output = await executeCommand('bun', ['run', tempFile], 5000);
        const result = JSON.parse(output.trim());
        return createTextResult(`Result: ${JSON.stringify(result, null, 2)}`);
      } finally {
        await unlink(tempFile).catch(() => {});
      }
    } catch (error) {
      logger.error('Failed to evaluate expression', error);
      return createErrorResult(error as Error);
    }
  },
};

